﻿Imports System.IO
Imports System.Net
Imports FTTK.Exceptions
Imports FTTK.Json
Imports System.Diagnostics.Contracts
Imports System.Text

Namespace FTTK.Services.Implementations.Rest
    Public Class RestRequester
        Public Sub New(apiKey As String)
            Contract.Requires(Of ArgumentNullException)(Not String.IsNullOrEmpty(apiKey), "Apikey must be not null")
            Me._ApiKey = apiKey
        End Sub

        Private _ApiKey As String

        Public Property ApiKey As String
            Get
                Return Me._ApiKey
            End Get
            Set(value As String)
                Me._ApiKey = value
            End Set
        End Property

#If SILVERLIGHT = 0 Then
        Public Function GetResponse(url As String,
                                    Optional sessionId As String = Nothing,
                                    Optional httpMethod As String = "GET",
                                    Optional postData As String = Nothing) As String
            Contract.Requires(Of ArgumentNullException)(Not String.IsNullOrEmpty(url), "Url must be not null")
            Dim retStr As String = Nothing
            Dim response As HttpWebResponse = GetResponseInner(url, sessionId, httpMethod, postData)
            Try
                Using dataStream = response.GetResponseStream()
                    Using reader = New StreamReader(dataStream)
                        retStr = reader.ReadToEnd()
                    End Using
                End Using
            Catch ex As Exception
                Throw 
            Finally
                If response IsNot Nothing Then response.Close()
            End Try
            Return retStr
        End Function

        Public Function GetResponseBytes(url As String, Optional httpMethod As String = "GET", Optional postData As String = Nothing) As Byte()
            Contract.Requires(Of ArgumentNullException)(Not String.IsNullOrEmpty(url), "Url must be not null")
            Dim retBytes As Byte()
            Dim response As HttpWebResponse = GetResponseInner(url, Nothing, httpMethod, postData)
            Try
                Using dataStream = response.GetResponseStream()
                    Using memStream = New MemoryStream()
                        Dim buffer(1023) As Byte
                        Dim readedByte As Integer
                        Dim index = 0
                        Do
                            readedByte = dataStream.ReadByte()
                            If readedByte >= 0 Then
                                memStream.WriteByte(CType(readedByte, Byte))
                            End If
                        Loop While readedByte >= 0
                        retBytes = memStream.ToArray()
                    End Using
                End Using
            Catch ex As Exception
                Throw
            Finally
                If response IsNot Nothing Then response.Close()
            End Try
            Return retBytes
        End Function

        Private Function GetResponseInner(url As String,
                                          sessionId As String,
                                          httpMethod As String,
                                          postData As String) As HttpWebResponse
            Contract.Requires(Of ArgumentNullException)(Not String.IsNullOrEmpty(url), "Url must be not null")
            Dim request = CType(WebRequest.Create(url), HttpWebRequest)
            request.ContentType = "application/x-www-form-urlencoded"
            request.Method = httpMethod
            Me.AddAuthenticationHeader(request, ApiKey, sessionId)
            If httpMethod = "POST" AndAlso postData IsNot Nothing Then
                Dim bytes As Byte() = encoding.UTF8.GetBytes(postData)
                request.ContentLength = bytes.Length
                Dim newStream As Stream = request.GetRequestStream()
                newStream.Write(bytes, 0, bytes.Length)
            End If


            Dim response As HttpWebResponse = Nothing
            Try
                response = CType(request.GetResponse(), HttpWebResponse)
            Catch ex As Exception
                Throw ex
            End Try
            Return response
        End Function
#End If

        Public Sub GetResponseAsync(url As String,
                                    callback As Action(Of String, Exception, Object),
                                    userState As Object,
                                    Optional httpMethod As String = "GET",
                                    Optional postData As String = Nothing)
            GetResponseAsync(url, Nothing, callback, userState, httpMethod, postData)
        End Sub

        Public Sub GetResponseAsync(url As String,
                                    sessionId As String,
                                    callback As Action(Of String, Exception, Object),
                                    userState As Object,
                                    Optional httpMethod As String = "GET",
                                    Optional postData As String = Nothing)
            Dim request = CType(WebRequest.Create(url), HttpWebRequest)
            request.Method = httpMethod
            request.ContentType = "application/x-www-form-urlencoded"
            Me.AddAuthenticationHeader(request, ApiKey, sessionId)
            Try
                If httpMethod = "POST" AndAlso postData IsNot Nothing Then
                    Dim result = request.BeginGetRequestStream(AddressOf GetRequestStreamCallback, New Tuple(Of HttpWebRequest, Action(Of String, Exception, Object), Object, String)(request, callback, userState, postData))
                Else
                    Dim result = request.BeginGetResponse(AddressOf GetResponseCallback, New Tuple(Of HttpWebRequest, Action(Of String, Exception, Object), Object)(request, callback, userState))
                End If
            Catch ex As Exception
                Throw ex
            End Try
        End Sub

        Private Shared Sub GetRequestStreamCallback(ByVal asynchronousResult As IAsyncResult)
            Dim tuple = CType(asynchronousResult.AsyncState, Tuple(Of HttpWebRequest, Action(Of String, Exception, Object), Object, String))
            Dim request As HttpWebRequest = tuple.Item1
            Dim getStream As Stream = request.EndGetRequestStream(asynchronousResult)
            Dim postData = tuple.Item4
            Dim byteArray As Byte() = Encoding.UTF8.GetBytes(postData)
            getStream.Write(byteArray, 0, postData.Length)
            getStream.Close()
            Dim newTuple = New Tuple(Of HttpWebRequest, Action(Of String, Exception, Object), Object)(tuple.Item1, tuple.Item2, tuple.Item3)
            Dim result As IAsyncResult = CType(request.BeginGetResponse(AddressOf GetResponseCallback, newTuple), IAsyncResult)
        End Sub

        Private Shared Sub GetResponseCallback(ByVal asynchronousResult As IAsyncResult)
            Dim tuple = CType(asynchronousResult.AsyncState, Tuple(Of HttpWebRequest, Action(Of String, Exception, Object), Object))
            Dim request As HttpWebRequest = tuple.Item1
            Try
                Dim response As HttpWebResponse = CType(request.EndGetResponse(asynchronousResult), HttpWebResponse)
                Dim responseString As String
                Using dataStream = response.GetResponseStream()
                    Using reader = New StreamReader(dataStream)
                        responseString = reader.ReadToEnd()
                    End Using
                End Using
                tuple.Item2.Invoke(responseString, Nothing, tuple.Item3)
            Catch ex As Exception
                tuple.Item2.Invoke(Nothing, ex, tuple.Item3)
            End Try

        End Sub

        Public Sub GetResponseBytesAsync(url As String,
                                        callback As Action(Of Byte(), Exception, Object),
                                        userState As Object,
                                        Optional httpMethod As String = "GET",
                                        Optional postData As String = Nothing)
            Dim request = CType(WebRequest.Create(url), HttpWebRequest)
            request.Method = httpMethod
            request.ContentType = "application/x-www-form-urlencoded"
            Me.AddAuthenticationHeader(request, ApiKey, Nothing)
            Try
                If httpMethod = "POST" AndAlso postData IsNot Nothing Then
                    Dim result = request.BeginGetRequestStream(AddressOf GetRequestBytesStreamCallback, New Tuple(Of HttpWebRequest, Action(Of Byte(), Exception, Object), Object, String)(request, callback, userState, postData))
                Else
                    Dim result = request.BeginGetResponse(AddressOf GetResponseBytesCallback, New Tuple(Of HttpWebRequest, Action(Of Byte(), Exception, Object), Object)(request, callback, userState))
                End If
            Catch ex As Exception
                Throw ex
            End Try
        End Sub

        Private Shared Sub GetRequestBytesStreamCallback(ByVal asynchronousResult As IAsyncResult)
            Dim tuple = CType(asynchronousResult.AsyncState, Tuple(Of HttpWebRequest, Action(Of Byte(), Exception, Object), Object, String))
            Dim request As HttpWebRequest = tuple.Item1
            Dim getStream As Stream = request.EndGetRequestStream(asynchronousResult)
            Dim postData = tuple.Item4
            Dim byteArray As Byte() = Encoding.UTF8.GetBytes(postData)
            getStream.Write(byteArray, 0, postData.Length)
            getStream.Close()
            Dim newTuple = New Tuple(Of HttpWebRequest, Action(Of Byte(), Exception, Object), Object)(tuple.Item1, tuple.Item2, tuple.Item3)
            Dim result As IAsyncResult = CType(request.BeginGetResponse(AddressOf GetResponseBytesCallback, newTuple), IAsyncResult)
        End Sub

        Private Shared Sub GetResponseBytesCallback(ByVal asynchronousResult As IAsyncResult)
            Dim tuple = CType(asynchronousResult.AsyncState, Tuple(Of HttpWebRequest, Action(Of Byte(), Exception, Object), Object))
            Dim request As HttpWebRequest = tuple.Item1
            Try
                Dim response As HttpWebResponse = CType(request.EndGetResponse(asynchronousResult), HttpWebResponse)
                Dim responseBytes() As Byte
                Using dataStream = response.GetResponseStream()
                    Using memStream = New MemoryStream()
                        Dim buffer(1023) As Byte
                        Dim readedByte As Integer
                        Dim index = 0
                        Do
                            readedByte = dataStream.ReadByte()
                            If readedByte >= 0 Then
                                memStream.WriteByte(CType(readedByte, Byte))
                            End If
                        Loop While readedByte >= 0
                        responseBytes = memStream.ToArray()
                    End Using
                End Using
                tuple.Item2.Invoke(responseBytes, Nothing, tuple.Item3)
            Catch ex As Exception
                tuple.Item2.Invoke(Nothing, ex, tuple.Item3)
            End Try

        End Sub

        Private Sub AddAuthenticationHeader(request As HttpWebRequest, apiKey As String, sessionId As String)
            Dim credentials As String
            If String.IsNullOrEmpty(sessionId) Then
                credentials = String.Format("{0}:", apiKey)
            Else
                credentials = String.Format("{0}:{1}", apiKey, sessionId)
            End If
            Dim bytes = Encoding.UTF8.GetBytes(credentials)
            Dim base64 = Convert.ToBase64String(bytes)
            Dim authorization = String.Concat("Basic ", base64)
            request.Headers.Item("Authorization") = authorization
        End Sub

    End Class
End Namespace
